/*
Code : Dzlua
Email: 505544956@qq.com
Time : 2017/06/05
*/
#ifndef DZLUA_MULTI_SERVER_H_
#define DZLUA_MULTI_SERVER_H_

#pragma once

#include <event.h>
#include <event2/listener.h>
#include <event2/thread.h>

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
#include <io.h>
#include <fcntl.h>

#include <thread>

namespace dzlua {

namespace cs {

class MultiServer;
class Conn;
class ConnQueue;
struct LibeventThread;

/* 
    1. This class is a list of nodes, each connected node in the information storage,
        and provides read and write data interface.
    2. This class must be created by TcpBaseServer, and managed by the class ConnQueue.
*/
class Conn
{
    friend class ConnQueue;
    friend class MultiServer;
public:
    LibeventThread *GetThread();
    int GetFd();

    // get the length of the readable data.
    int GetReadBufferLen();

    /*
        Remove len bytes from the read buffer and stored in buffer,
            if not enough, then read all the data.
        Return the number bytes of data read.
    */
    int GetReadBuffer(char *buffer, int len);
    /*
        Copy len bytes from the read buffer and stored in buffer,
            if not enough, then all the data is copied.
        Return the number bytes of data copied.
        When the operation is performed, the data is left in the buffer,
            and the data in buffer is only a copy of the original data.
    */
    int CopyReadBuffer(char *buffer, int len);

    // Get the length of writable data.
    int GetWriteBufferLen();

    // Adding data to the write buffer, ready to send.
    int AddToWriteBuffer(char *buffer, int len);

    // Move the data in the read buffer to the write buffer.
    void MoveBufferData();
private:
    const int m_fd;             // id of socket
    evbuffer *m_ReadBuf;        // read data buffer
    evbuffer *m_WriteBuf;       // write data buffer
    Conn *m_Prev;               // the pointer to the previous node
    Conn *m_Next;               // the pointer to the next node
    LibeventThread *m_Thread;   //
private:
    Conn(int fd=0);
    ~Conn();
};

/*
    Double linked list with head and tail nodes,
        each node stores a connected data.
*/
class ConnQueue
{
public:
    ConnQueue();
    ~ConnQueue();
public:
    Conn *InsertConn(int fd, LibeventThread *t);
    void DeleteConn(Conn *c);
private:
    Conn *m_head;
    Conn *m_tail;
};

/*
    Thread information for each child thread.
*/
struct LibeventThread
{
public:
    std::shared_ptr<std::thread> td; // thread.
    struct event_base *base;         // event handler of libevent.
    struct event notifyEvent;        // listener managed event.
    evutil_socket_t notifyReceiveFd; // Receiving end of pipe.
    evutil_socket_t notifySendFd;    // Sending end of pipe.
    ConnQueue connectQueue;          // Socket linked list.

    /*
        Many callback functions are used in the event handling of libevent,
            and class implicit this pointers cannot be used,
            So pass the pointer of TcpBaseServer in this way.
    */
    MultiServer *tcpConnect;
};

class MultiServer
{
public:
    MultiServer(int count);
    ~MultiServer();
public:

    /*
        Set the port number for listening.
        If you don't need to listen, set it to EXIT_CODE.
    */
    void SetPort(int port);

    // Start event loop.
    bool StartRun();

    /*
        End the event loop in tv time.
        if tv is empty, stop immediately.
    */
    void StopRun(timeval *tv);

    /*
        Adding and deleting signal processing events.
        sig is the signal, and ptr is the function to call back.
    */
    bool AddSignalEvent(int sig, void (*ptr)(int, short, void*));
    bool DeleteSignalEvent(int sig);

    /*
        Add and delete timed events.
        
        ptr is the function to call back,
        tv is the interval,
        once determines whether to execute it only once.
    */
    event *AddTimerEvent(void(*ptr)(int, short, void*), timeval tv, bool once);
    bool DeleteTImerEvent(event *ev);

protected:
    /*
        It is generally necessary to inherit and deal with the specific business
    */

    // When the new connection is successful, the function is called. 
    virtual void ConnectionEvent(Conn *conn) {}

    // After reading the data, the function is called.
    virtual void ReadEvent(Conn *conn) {}

    /* 
        The function is called after the completion of the call.
        Because of a string problem, it is not called every time the data is sent.
    */
    virtual void WriteEvent(Conn *conn) {}

    /*
        This function is called when the connection is disconnected,
            or the customer is automatically disconnected.
    */
    virtual void CloseEvent(Conn *conn, short events) {}

private:
    // Initializes the data of the child thread.
    void SetupThread(LibeventThread *ltd);

    // The entrance function of child thread.
    static void WorkerLibevent(void *arg);

    /*
        After the primary thread receives the request.
        It corresponds to the processing function of the child thread.
    */
    static void ThreadProcess(int fd, short which, void *arg);

    // the callbacks called by libevent
    static void ListenerEventCb(struct evconnlistener *listener, evutil_socket_t fd, sockaddr *sa, int socklen, void *user_data);
    static void ReadEventCb(struct bufferevent *bev, void *data);
    static void WriteEventCb(struct bufferevent *bev, void *data);
    static void CloseEventCb(struct bufferevent *bev, short events, void *data);

private:
    static const int EXIT_CODE = -1;
    static const int MAX_SIGNAL = 256;
private:
    int m_ThreadCount;                 // child thread count.
    int m_Port;                        // Listening port.
    LibeventThread *m_MainBase;        // The libevent event processor for the primary thread.
    LibeventThread *m_Threads;         // The array stores information about each child thread.
    event *m_SignalEvents[MAX_SIGNAL]; // Custom signal processing.
};

} // end namespace cs

} // end namespace dzlua

#endif // end define DZLUA_MULTI_SERVER_H_